#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import unittest
from bzjsons import Jsoner
import json
import datetime

__project_ = 'bzjsons'
__file_name__ = 'dumps_test'
__author__ = 'bright.zhang'
__time__ = '2020/6/23 0:08'


class SalesOrder:
    def __init__(self, order_id=None, customer=None, goods=None, amount=None, on=None):
        self.order_id = order_id
        self.customer = customer
        self.goods = goods
        self.amount = amount
        self.on = on
        self.deleted = False


class Customer:
    def __init__(self, customer_id=None, name=None, tel=None, addr=None):
        self.customer_id = customer_id
        self.name = name
        self.tel = tel
        self.addr = addr


class Goods:
    def __init__(self, goods_id=None, goods_name=None, price=None, tags=None):
        self.goods_id = goods_id
        self.goods_name = goods_name
        self.price = price
        self.tags = tags


class Goods2:
    def __init__(self, goods_id=None, goods_name=None, price=None):
        self.goods_id = goods_id
        self.goods_name = goods_name
        self.price = price
        self.tags = None

    def set_tags(self, tags):
        if not isinstance(tags, dict):
            raise AttributeError("Invalid tags assigned, please make sure it's a dict.")
        self.tags = tags


class Tag:
    def __init__(self, tag_id=None, name=None, enabled=None):
        self.tag_id = tag_id
        self.tag_name = name
        self.enabled = enabled


class BzjsonsDumpsTest(unittest.TestCase):
    def test_case_031__dumps_basic_type__int(self):
        a = 100
        r1 = Jsoner.dumps(a)
        r2 = json.dumps(a)
        self.assertEqual("100", r1, "not euqlas to \"100\".")
        self.assertEqual(r2, r1, "different with json.dumps().")

    def test_case_032__dumps__basic_type__float(self):
        f1 = 123.456
        r1 = Jsoner.dumps(f1)
        r2 = json.dumps(f1)
        self.assertEqual("123.456", r1, "not equals")
        self.assertEqual(r2, r1, "different with json.dumps().")

    def test_case_033__dumps__basic_type__str(self):
        s1 = "abc你好"
        r1 = Jsoner.dumps(s1)
        r2 = json.dumps(s1, ensure_ascii=False)
        # 涉及中文的json序列化，Jsoner.dumps和json.dumps在ensure_ascii上正好相反
        # self.assertEqual(r1, 'abc\u4f60\u597d', "not equals")
        self.assertEqual(r2, r1, "different with json.dumps().")

    def test_case_034__dumps__basic_type__none(self):
        s1 = None
        r1 = Jsoner.dumps(s1)
        r2 = json.dumps(s1)
        self.assertEqual(r2, r1, "different with json.dumps().")
        self.assertEqual('null', r1, 'incorrect')

    def test_case_035__dumps__basic_type__bool(self):
        b1 = True
        b2 = False
        r11 = Jsoner.dumps(b1)
        r12 = json.dumps(b1)
        self.assertEqual(r12, r11, "1: different with json.dumps().")
        r21 = Jsoner.dumps(b2)
        r22 = json.dumps(b2)
        self.assertEqual(r22, r21, "2: different with json.dumps().")

    def test_case_036__dumps__basic_type_datetime(self):
        from datetime import datetime
        d1 = datetime.strptime('2020-02-02 02:02:02', '%Y-%m-%d %H:%M:%S')
        r1 = Jsoner.dumps(d1)
        r2 = json.dumps(d1)
        self.assertEqual(r2, r1, "different with json.dumps().")
        self.assertEqual('"2020-02-02 02:02:02"', r1, 'incorrect')

    def test_case_037__dumps__basic_type__date(self):
        from datetime import datetime
        d1 = datetime.strptime('2020-02-02', '%Y-%m-%d').date()
        r1 = Jsoner.dumps(d1)
        r2 = json.dumps(d1)
        self.assertEqual(r2, r1, "different with json.dumps().")
        self.assertEqual('"2020-02-02"', r1, 'incorrect')

    def test_case_038__dumps__basic_type__none(self):
        n1 = None
        r1 = Jsoner.dumps(n1)
        r2 = json.dumps(n1)
        print('json.dumps(None):', r2)
        self.assertEqual(r2, r1, "different with json.dumps().")
        self.assertEqual('null', r1, 'incorrect')

    # list, tuple, dict, set
    def test_case_039__dumps__collection_type__list(self):
        l1 = [1, 2, 'a', 20.233]
        r1 = Jsoner.dumps(l1)
        r2 = json.dumps(l1)
        self.assertEqual(r2, r1, 'different with json.dumps().')

    def test_case_040__dumps__collection_type__tuple(self):
        t1 = (1, 2, 'abc', 20.33)
        r1 = Jsoner.dumps(t1)
        r2 = json.dumps(t1)
        self.assertEqual(r2, r1, 'different with json.dumps().')

    def test_case_041__dumps__collection_type__dict(self):
        d1 = dict()
        d1['a'] = 'aaaaa'
        d1[2] = 222
        d1[1] = 111
        r1 = Jsoner.dumps(d1)
        r2 = json.dumps(d1)
        self.assertEqual(r2, r1, 'different with json.dumps().')

    def test_case_042__dumps__collection_type__set(self):
        s1 = set()
        s1.add('a')
        s1.add(100)
        s1.add(3.1415926)
        r1 = Jsoner.dumps(s1)
        self.assertRaises(TypeError, json.dumps, s1)

        try:
            json.dumps(s1)
        except TypeError:
            print('json.dumps() does not support to serialize "set" as expected.')

        print('r1:', r1)
        self.assertTrue(r1 in('["a", 3.1415926, 100]', '["a", 100, 3.1415926]', '[3.1415926, "a", 100]', '[3.1415926, 100, "a"]', '[100, "a", 3.1415926]', '[100, 3.1415926, "a"]'), "incorrect")

    def test_case_043__dumps__simple_custom_class(self):
        c1 = Customer(100, 'bright', '13988888888', '广州')
        r1 = Jsoner.dumps(c1, ensure_ascii=True)
        r2 = Jsoner.dumps(c1)
        print(r1)
        self.assertEqual("{\"customer_id\": 100, \"name\": \"bright\", \"tel\": \"13988888888\", \"addr\": \"\\u5e7f\\u5dde\"}", r1, 'incorrect')
        self.assertEqual("{\"customer_id\": 100, \"name\": \"bright\", \"tel\": \"13988888888\", \"addr\": \"广州\"}", r2, 'incorrect')

        # import ujson
        # ur1 = ujson.dumps(c1)
        # print('r1 in ujson:', ur1)
        # 此处ujson报：TypeError: <dumps_test.Customer object at 0x0000016568CA6CA0> is not JSON serializable

    def test_case_044__dumps__empty_custom_class(self):
        c = Customer()
        r1 = Jsoner.dumps(c)
        self.assertEqual('{\"customer_id\": null, \"name\": null, \"tel\": null, \"addr\": null}', r1, 'incorrect')

    def test_case_045__dumps__simple_custom_class_list(self):
        cl1 = [Customer(100, 'bright', '13988888888', 'guangzhou'), Customer(101, 'lucy', '13999999999', 'hefei')]
        r1 = Jsoner.dumps(cl1)
        expected_json = '[{\"customer_id\": 100, \"name\": \"bright\", \"tel\": \"13988888888\", \"addr\": \"guangzhou\"}, {\"customer_id\": 101, \"name\": \"lucy\", \"tel\": \"13999999999\", \"addr\": \"hefei\"}]'
        # print(r1)
        self.assertEqual(expected_json, r1, 'incorrect')

    # first serialize the SalesOrder then deserialize it, to check if it can be backed to the original one.
    # if YES, it means successful serialized, otherwise FAILED.
    def test_case_046__dumps__nested_custom_class(self):
        import datetime
        customer01 = Customer('C001', 'bright', '1372388888', '广州')
        goods1_1 = Goods('G001', '海苔', 25.00)
        goods1_2 = Goods('G002', '手机', 2588.00)
        so1 = SalesOrder('SO-001', customer01, [goods1_1, goods1_2], 1200.38, datetime.datetime(2020, 6, 23, 1, 21, 57))
        r1 = Jsoner.dumps(so1)
        print(r1)
        kw = dict()
        kw["customer"] = Customer
        kw["goods"] = Goods
        so1_2 = Jsoner.loads_as_instance(r1, cls=SalesOrder, **kw)
        self.assertEqual(so1.order_id, so1_2.order_id, 'incorrect order_id')
        self.assertEqual(so1.amount, so1_2.amount, 'incorrect amount')
        self.assertEqual(so1.on, so1_2.on, 'incorrect on')
        self.assertEqual(so1.customer.customer_id, so1_2.customer.customer_id, 'incorrect customer-id')
        self.assertEqual(so1.customer.name, so1_2.customer.name, 'incorrect customer-name')
        self.assertEqual(so1.customer.tel, so1_2.customer.tel, 'incorrect customer-tel')
        self.assertEqual(so1.customer.addr, so1_2.customer.addr, 'incorrect customer-addr')
        self.assertEqual(len(so1.goods), len(so1_2.goods), 'incorrect goods count')
        self.assertEqual(so1.goods[0].goods_id, so1_2.goods[0].goods_id, 'incorrect goods-id')
        self.assertEqual(so1.goods[0].goods_name, so1_2.goods[0].goods_name, 'incorrect goods_name')
        self.assertEqual(so1.goods[0].price, so1_2.goods[0].price, 'incorrect goods-price')
        self.assertEqual(so1.goods[1].goods_id, so1_2.goods[1].goods_id, 'incorrect goods-id')
        self.assertEqual(so1.goods[1].goods_name, so1_2.goods[1].goods_name, 'incorrect goods_name')
        self.assertEqual(so1.goods[1].price, so1_2.goods[1].price, 'incorrect goods-price')

    def test_case_047__dumps__complex_custom_class(self):
        customer01 = Customer('C001', 'bright', '1372388888', '广州')
        goods1_1 = Goods('G001', '海苔', 25.00)
        goods1_2 = Goods('G002', '手机', 2588.00)
        so1 = SalesOrder('SO-001', customer01, [goods1_1, goods1_2], 1200.38, datetime.datetime.now())

        customer02 = Customer('C002', 'yoyo', '1362388888', '武汉')
        goods2_1 = Goods('G003', '绿瘦子', 258.00)
        goods2_2 = Goods('G004', '泡泡水', 9.98)
        so2 = SalesOrder('SO-002', customer02, [goods2_1, goods2_2], 3888.38, datetime.datetime.now())

        so_list = [so1, so2]
        so_list_dict = Jsoner._to_dict(so_list)
        print(so_list_dict)

        so_list_json = Jsoner.dumps(so_list)
        print('serialized:', so_list_json)

        so_list_r = Jsoner.loads(so_list_json)
        print('deserialized:', so_list_r)

        kw = dict()
        kw['customer'] = Customer
        kw['goods'] = Goods

        so_list_rr = Jsoner.loads_as_instance(so_list_json, cls=SalesOrder, **kw)
        print(so_list_rr)

        self.assertEqual(len(so_list), len(so_list_rr), 'incorrect sales order\'s count.')
        self.assertEqual(so_list[0].amount, so_list_rr[0].amount, 'incorrect sales order[0]\'s amount.')

    def test_case_048_dumps_chinese(self):
        customer01 = Customer('C001', 'bright', '1372388888', '广州')
        json1 = Jsoner.dumps(customer01, ensure_ascii=True)
        json2 = Jsoner.dumps(customer01)
        expected1 = '{\"customer_id\": \"C001\", \"name\": \"bright\", \"tel\": \"1372388888\", \"addr\": \"\u5e7f\u5dde\"}'
        expected1_ascii = ascii(expected1)
        expected2 = '{\"customer_id\": \"C001\", \"name\": \"bright\", \"tel\": \"1372388888\", \"addr\": \"广州\"}'
        self.assertEqual(expected1, expected2, 'expected1 not equals to expected2')
        expected2_encoded = ascii(expected2)
        print('expected1:', expected1, type(expected1))
        # print('expected2:', expected2, type(expected2))
        # print('expected2_encoded:', expected2_encoded, type(expected2_encoded))
        # TODO: eval可以去掉收尾的单引号，但是对于中文也字段做了转码显示，待确认
        # self.assertEqual(eval(expected1_ascii), json1, 'incorrect json1')
        # 手动给json1的收尾加上单引号：'json1'
        json1_quoted = '{1}{0}{1}'.format(json1, "'")
        # TODO: 针对json1(ensure_ascii=True)的断言须注意，因为期望值的字符串在比较时会自动转换为中文显示，所以会导致断言失败
        self.assertEqual(expected1_ascii, json1_quoted, 'incorrect json1')
        print('expected1_ascii:', expected1_ascii)
        print('json1_quoted:', json1_quoted)
        self.assertEqual(expected2, json2, 'incorrect json2')

    def test_case_049_datetime_list_dumps(self):
        d1 = datetime.datetime(2020, 6, 23, 1, 21, 57)
        d2 = datetime.datetime(2020, 6, 24, 1, 21, 57)
        ds = [d1, d2]
        ds_json = Jsoner.dumps(ds)
        self.assertEqual('[\"2020-06-23 01:21:57\", \"2020-06-24 01:21:57\"]', ds_json, 'incorrect dumps for datetime list.')

    def test_case_050_date_list_dumps(self):
        d1 = datetime.date(2020, 6, 23)
        d2 = datetime.date(2020, 6, 24)
        ds = [d1, d2]
        ds_json = Jsoner.dumps(ds)
        self.assertEqual('[\"2020-06-23\", \"2020-06-24\"]', ds_json, 'incorrect dumps for datetime list.')

    def test_case_051__dumps__complex_custom_class2(self):
        customer01 = Customer('C001', 'bright', '1372388888', '广州')
        goods1_1 = Goods('G001', '海苔', 25.00)
        goods1_2 = Goods('G002', '手机', 2588.00)
        so1 = SalesOrder('SO-001', customer01, [goods1_1, goods1_2], 1200.38, datetime.datetime.now())
        lingshi = Tag(1, '零食', True)
        shiwu = Tag(0, '食物', True)
        goods1_1.tags = [shiwu, lingshi]

        customer02 = Customer('C002', 'yoyo', '1362388888', '武汉')
        goods2_1 = Goods('G003', '绿瘦子', 258.00)
        goods2_2 = Goods('G004', '泡泡水', 9.98)
        so2 = SalesOrder('SO-002', customer02, [goods2_1, goods2_2], 3888.38, datetime.datetime.now())

        so_list = [so1, so2]
        so_list_dict = Jsoner._to_dict(so_list)
        print('so_list_dict:', so_list_dict)

        so_list_json = Jsoner.dumps(so_list)
        print('serialized:', so_list_json)

        so_list_r = Jsoner.loads(so_list_json)
        print('deserialized:', so_list_r)

        kw = dict()
        kw['customer'] = Customer
        kw['goods'] = Goods
        kw['tags'] = Tag

        so_list_rr = Jsoner.loads_as_instance(so_list_json, cls=SalesOrder, **kw)
        print('so_list_rr:', so_list_rr)

        self.assertEqual(len(so_list), len(so_list_rr), 'incorrect sales order\'s count.')
        self.assertEqual(so_list[0].amount, so_list_rr[0].amount, 'incorrect sales order[0]\'s amount.')

    # The loads_as_instance part would be moved to the loads_test.py later.
    def test_case_052__dumps__complex_custom_class3(self):
        g1 = Goods2(1, '砀山梨', 8.88)
        tags1 = dict()
        tags1['brand'] = '好吃牌'
        tags1['origin'] = '砀山'
        g1.set_tags(tags1)
        g1_json = Jsoner.dumps(g1)
        print(g1_json)
        self.assertIsNotNone(g1_json)

        # import ujson
        # g1_ujson = ujson.dumps(g1)
        # print(g1_ujson)
        # self.assertEqual(g1_json, g1_ujson, 'not matched between Jsoner and ujson.')
        # 此处usjon报：TypeError: <dumps_test.Goods2 object at 0x000001EAF5C56CA0> is not JSON serializable

        g1_deserialized = Jsoner.loads_as_instance(g1_json, cls=Goods2)
        print(g1_deserialized)

    def test_case_053_reserved_chars(self):
        # ujson.dumps("<script>John&Doe", encode_html_chars=True)
        # '"\\u003cscript\\u003eJohn\\u0026Doe"'
        rc1 = "<script>John&Doe"
        rc1_jsoner = Jsoner.dumps(rc1)
        self.assertEqual('"<script>John&Doe"', rc1_jsoner, 'incorrect')

        rc2 = "{abc}"
        rc2_jsoner = Jsoner.dumps(rc2)
        self.assertEqual('"{abc}"', rc2_jsoner)

        customer01 = Customer('C001', 'bright', '1372388888', '{广州}')
        customer01_jsoner = Jsoner.dumps(customer01)
        self.assertEqual('{"customer_id": "C001", "name": "bright", "tel": "1372388888", "addr": "{广州}"}', customer01_jsoner)

        rc3 = "/abc\\"
        rc3_jsoner = Jsoner.dumps(rc3)
        # print(rc3_jsoner)
        self.assertEqual('"/abc\\\\\"', rc3_jsoner)

